#include "fstraverseiterator.h"

bool QFileInfoLess::operator()(const QFileInfo & first,
                               const QFileInfo & second) const
{
    bool
        rv = first.absoluteFilePath() < second.absoluteFilePath();

    return rv;
}




FSGraph::FSGraph(bool filesAllowed)
    : m_filesAllowed(filesAllowed)
{
    ;
}




FSTraverseIterator::FSTraverseIterator(const QString  & startingAbsolutePath,
                                       int              flags)
    : m_graph(true)
    , m_flags(flags)
{
    QFileInfo
        startingPoint(startingAbsolutePath);
    if (startingPoint.exists())
    {
        setupFlagsAndColors();
        m_iterator = Iterator(m_graph,
                              startingPoint,
                              m_iteratorFlags);
        next(true);
    }
}


FSTraverseIterator::FSTraverseIterator(const QStringList  & startingAbsolutePaths,
                                       int                  flags)
    : m_graph(true)
    , m_flags(flags)
{
    QFileInfoList
        startingPoints;
    foreach (QString startingAbsolutePath, startingAbsolutePaths)
    {
        QFileInfo
            startingPoint(startingAbsolutePath);
        if (startingPoint.exists())
        {
            startingPoints.push_back(startingPoint);
        }
    }

    if (!startingPoints.empty())
    {
        setupFlagsAndColors();
        m_iterator = Iterator(m_graph,
                              startingPoints.begin(),
                              startingPoints.end(),
                              m_iteratorFlags);
        next(true);
    }
}


FSTraverseIterator::FSTraverseIterator()
    : m_graph(false)
    , m_flags(0)
{
    ;
}


QFileInfo * FSTraverseIterator::operator->()
{
    return &m_currentFileInfo;
}


QFileInfo & FSTraverseIterator::operator*()
{
    return m_currentFileInfo;
}

FSTraverseIterator & FSTraverseIterator::operator++()
{
    next();
    return *this;
}


FSTraverseIterator FSTraverseIterator::operator++(int)
{
    FSTraverseIterator
        tmp(*this);
    next();
    return tmp;
}


#define __yield_return(cn) {if(allowed(cn)){m_currentFileInfo=cn.m_node;break;}}
void FSTraverseIterator::next(bool initializationTime)
{
    Iterator
        endIt = Iterator();
    while (m_iterator != endIt)
    {
        // On creation, the m_iterator positions on the first
        // unseen item - no need to increment, but from that
        // point on, m_iterator either points to a seen item
        // or the "end".
        if (initializationTime)
        {
            initializationTime = false;
        }
        else
        {
            ++m_iterator;
        }

        if (m_iterator == endIt)
        {
            break;
        }

        ColoredNode<QFileInfo>
            coloredNode(*m_iterator);
        if (allowed(coloredNode))
        {
            __yield_return(coloredNode);
        }
    }
}
#undef __yield_return

void FSTraverseIterator::setupFlagsAndColors()
{
    // Here we are trying to map FSTraverseIterator::Flag flags
    // to Iterator::Flag flag values.
    //
    // enum Flag // <- FSTraverseIterator::Flag
    // {
    //     DirsPreOrder  = 1,
    //     DirsPostOrder = 2,
    //     DirsBoth      = DirPreOrder | DirPostOrder,
    //     Files         = 8,
    //     FilesAndDirs  = DirsPreOrder | Files
    // };

    // enum Flag // <- Iterator::Flag
    // {
    //     PreVisit = 1,
    //     PostVisit = 2
    // };
    //
    // The FSTraverseIterator::Flag flags control
    // what kind of content is interesting (files, directories
    // or both) as well as at what point directories should
    // be iterated (before visiting its files, after
    // visiting its files, or both times).
    //
    // Files (that are leaf nodes (nodes without further
    // adjacents) from the point of view of depth first search
    // don't care if they are preorder or postorder visited.
    // Therefore it's up to the directory visiting (pre, post
    // or both) to decide what kind of flags we give to the
    // generic depth-first search.

    m_iteratorFlags = Iterator::PreVisit;
    m_allowedDirColors = NC_GRAY;
    m_allowedFileColor = NC_GRAY;

    if (m_flags & DirsPostOrder)
    {
        if (m_flags & DirsPreOrder)
        {
            // dirs are to be iterated both pre- and postvisit times
            m_iteratorFlags |= Iterator::PostVisit;
            m_allowedDirColors |= NC_BLACK;
            // m_allowedFileColor does not change here - we don't
            // intend to receive duplicate iterations on files
        }
        else
        {
            // dirs are to be visited only at postvisit times
            m_iteratorFlags = Iterator::PostVisit;
            m_allowedDirColors  = NC_BLACK;
            // files will get iterated only at post visit times,
            // so we have better enable that
            m_allowedFileColor = NC_BLACK;
        }
    }
    else if (!(m_flags & DirsPreOrder))
    {
        // no directories are to be enumerated, after all (only traversed)
        m_allowedDirColors = 0;
    }

    if ((m_flags & Files) == 0)
    {
        // no files are to be enumerated, after all
        m_allowedFileColor = 0;
        m_graph = FSGraph(false);
    }
}


bool FSTraverseIterator::allowed(const ColoredNode<QFileInfo> & coloredNode)
{
    bool
        rv = false;

    if (coloredNode.m_node.isFile())
    {
        rv = m_allowedFileColor == coloredNode.m_color;
    }
    else // it's a directory
    {
        rv = m_allowedDirColors & coloredNode.m_color;
    }

    return rv;
}


bool operator==(const FSTraverseIterator & first,
                const FSTraverseIterator & second)
{
    typedef FSTraverseIterator::Iterator Iterator;

    return (first.m_iterator == Iterator() && second.m_iterator == Iterator())
           || (first.m_flags == second.m_flags
               && first.m_iterator == second.m_iterator
               && first.m_currentFileInfo == second.m_currentFileInfo
              );
}

bool operator!=(const FSTraverseIterator & first,
                const FSTraverseIterator & second)
{
    return !(first == second);
}
